/*
*	Copyright (C) 2014  Julio Montes, Mario Carrillo
*
*	Julio Montes e-mail: imc.coder@gmail.com
*
*	Mario Carrillo e-mail: mario_90ago@live.com.mx
*
*	BSD 3-Clause License
*/

#include "alliance/include/projectmgr.h"
#include "alliance/include/projectstree.h"
#include "alliance/include/path.h"
#include "alliance/include/alliancemenu.h"
#include "alliance/include/createprjdlg.h"
#include "alliance/include/createfiledlg.h"
#include "alliance/include/buildoptionsdlg.h"
#include "alliance/include/toolpanel.h"
#include "alliance/include/project.h"
#include "alliance/include/FileSelector.h"
#include "alliance/include/FileCreator.h"

#include "include/iaManager.h"

#include "sdk/include/filecfg.h"
#include "sdk/include/editormgr.h"
#include "sdk/include/mainframe.h"
#include "sdk/include/util.h"

#include <wx/dir.h>

#include <wx/wx.h>
#include <wx/aui/aui.h>
#include <wx/tokenzr.h>


namespace alliance
{
const wxString ProjectManager::s_projectExtension = _T( "allprj" );

wxBitmap ProjectManager::s_imageFile;

template<> ProjectManager *sdk::Mgr<ProjectManager>::instance = 0;

ProjectManager::ProjectManager()
    : m_activeProject( NULL )
    , m_projectsTree( NULL )

{
    s_imageFile = wxBitmap( Path::s_dirImages + _T( "image_file.png" ), wxBITMAP_TYPE_PNG );
}

ProjectManager::~ProjectManager()
{

}

void ProjectManager::ActiveProject( Project *project )
{
    if( !m_activeProject->IsBusy() )
    {
        if( m_activeProject->GetFilename() != project->GetFilename() )
        {
            m_projectsTree->ActivateProject( project );
            m_activeProject = project;
        }
    }
}

bool ProjectManager::Load()
{
    wxAuiNotebook *aui = iaManager::Get()->GetProjectNotebook();
    if( !aui )
    {
        //TODO: show error
        return false;
    }

    m_projectsTree = new ProjectsTree( aui, wxT( "Workspace" ) );
    aui->AddPage( m_projectsTree, wxT( "Projects" ) );
    if( !LoadMenu() )
    {
        //TODO: show error
        return false;
    }

    return true;
}

bool ProjectManager::LoadMenu()
{
    sdk::MainFrame *frame = sdk::Manager::GetMainFrame();
    if( !frame )
    {
        return false;
    }

    wxMenuBar *menuBar = frame->GetMenuBar();
    if( !menuBar )
    {
        return false;
    }

    const int indexProject = menuBar->FindMenu( wxT( "Project" ) );
    wxMenu *projectMenu;
    if( wxNOT_FOUND != indexProject )
    {
        projectMenu = menuBar->GetMenu( indexProject );
    }
    else
    {
        projectMenu = new wxMenu;
        menuBar->Insert( 4, projectMenu, wxT( "&Project" ) );
    }

    AllianceMenu *allianceMenu = new AllianceMenu;
    projectMenu->AppendSubMenu( allianceMenu, wxT( "alliance" ) );

    return true;
}

bool ProjectManager::CreateProject()
{
    CreateProjectDialog prjdlg( NULL );
    int result = prjdlg.ShowModal();

    if( wxID_OK != result )
    {
        return false;
    }

    const wxString &projectFilename = prjdlg.GetProjectFilename();

    Project *project = GetProject( projectFilename );
    //verify if the project is already loaded
    if( project )
    {
        result = wxMessageBox( wxT( "The project: " ) + projectFilename +
                               wxT( " is already open, you want replace this?" ),
                               wxT( "Create project" ), wxYES | wxNO | wxCANCEL | wxICON_QUESTION );

        //reload the project
        if( wxYES == result )
        {
            //Test if can write in this directory
            if( !wxRemoveFile( projectFilename ) )
            {
                //TODO: show message error, can't remove file
                return false;
            }
            if( !m_activeProject->ReloadFile( projectFilename ) )
            {
                //TODO: show error
                return false;
            }
            m_activeProject = project;
            m_projectsTree->ReloadProject( m_activeProject );
            m_projectsTree->ActivateProject( m_activeProject );
        }
    }
    else
    {
        if( wxFileExists( projectFilename ) )
        {
            result = wxMessageBox( wxT( "The project: " ) + projectFilename +
                                   wxT( " already exists, you want replace this?" ),
                                   wxT( "Create project" ), wxYES | wxNO | wxCANCEL | wxICON_QUESTION );

            if( wxYES != result )
            {
                return false;
            }
            //Test if can write in this directory
            if( !wxRemoveFile( projectFilename ) )
            {
                //TODO: show message error, can't remove file
                return false;
            }
        }

        m_activeProject = &( m_mapProjects[projectFilename] = Project() );
        if( !m_activeProject->LoadFile( projectFilename ) )
        {
            //TODO: show error
            return false;
        }

        m_projectsTree->AddProject( m_activeProject );
        m_projectsTree->ActivateProject( m_activeProject );
    }

    return true;
}

void ProjectManager::NewSourceFile()
{
    if( !m_activeProject )
    {
        //TODO: show error, you must first create a project
        return;
    }

    CreateFileDialog dlg( NULL );

    int result = dlg.ShowModal();

    if( result != wxID_OK )
    {
        return;
    }

    const wxString &filename = dlg.GetFileName();
    const wxString &virtualFolder = dlg.GetVirtualFolder();
    const wxString filePath = m_activeProject->GetDirectory() + filename;


    if( wxFileExists( filePath ) )
    {
        result = wxMessageBox( wxT( "The file: " ) + filePath +
                               wxT( " already exists, you want replace this?" ),
                               wxT( "Create file" ), wxYES | wxNO | wxCANCEL | wxICON_QUESTION );

        if( result != wxYES )
        {
            return;
        }
    }

    if( sdk::mkFile( filePath ) )
    {
        m_activeProject->AddFile( filePath, virtualFolder );
        m_projectsTree->AddFile( filePath, virtualFolder, m_activeProject );
    }
}







wxArrayString ProjectManager::GetVhdlFiles()
{
    wxArrayString VhdlFiles;
    VhdlFiles.Clear();

    Project *project;
    project=ProjectManager::GetActiveProject();
    const std::vector<projectFile> &vectorFiles = project->GetFiles();

    for( size_t i=0; i<vectorFiles.size(); ++i )
    {
        const wxString &filename = vectorFiles[i].m_filename.AfterLast( _T( '/' ) );

        if(filename.Contains(wxT(".vhdl")) || filename.Contains(wxT(".vhd")))
        {
            VhdlFiles.Add(filename);
        }

    }

    return VhdlFiles;

}



//----------------------------------------------------------
//----------------------------------------------------------
//----------------------------------------------------------


void ProjectManager::NewPatFile()

{
    if( !m_activeProject )
    {
        //TODO: show error, you must first create a project
        return;
    }

    wxArrayString SourceFiles;
    string FileNameIn;
    string FileNameOut;
    wxString F_outFormatWx;
    bool errors;
    Project *project;
    project=ProjectManager::GetActiveProject();
    unsigned int TypeFile=0;
    SourceFiles=this->GetVhdlFiles();

    if(!SourceFiles.Count())
    {
        wxMessageBox(wxT("There aren't vhdl files"), wxT("Wait"), wxICON_INFORMATION);
        return;
    }

    const wxString &dir = project->GetDirectory();
    FileSelector fs(wxT("Choose File") ,SourceFiles,dir,wxT("FileName (whit extension example: adder.pat)"),
                    TypeFile,FileNameIn,FileNameOut,F_outFormatWx);
    fs.ShowModal();

    if(FileNameOut.empty())
    {

        return;
    }

    else
    {
        char *FileNameInC = strdup(FileNameIn.c_str());
        char *FileNameOutC = strdup(FileNameOut.c_str());
        FileCreator FiCr(wxT("File Creator PAT"),FileNameInC,FileNameOutC,TypeFile);
        FiCr.ShowModal();
        errors=FiCr.GetErrorFileCreation();
    }

    if(!errors){
    m_activeProject->AddFile( F_outFormatWx, wxT("src/") );//virtualFolder
    m_projectsTree->AddFile( F_outFormatWx, wxT("src/"), m_activeProject );
    }

}



//

void ProjectManager::NewIocFile()
{
    if( !m_activeProject )
    {
        //TODO: show error, you must first create a project
        return;
    }

    wxArrayString SourceFiles;
    string FileNameIn;
    string FileNameOut;
    wxString F_outFormatWx;
    bool errors;
    Project *project;
    project=ProjectManager::GetActiveProject();
    unsigned int TypeFile=1;
    SourceFiles=this->GetVhdlFiles();

    if(!SourceFiles.Count())
    {
        wxMessageBox(wxT("There aren't vhdl files"), wxT("Wait"), wxICON_INFORMATION);
        return;
    }

    const wxString &dir = project->GetDirectory();
    FileSelector fs(wxT("Choose File") ,SourceFiles,dir,wxT("FileName (whit extension example: adder.ioc)"),
                    TypeFile,FileNameIn,FileNameOut,F_outFormatWx);
    fs.ShowModal();

    if(FileNameOut.empty())
    {

        return;
    }

    else
    {
        char *FileNameInC = strdup(FileNameIn.c_str());
        char *FileNameOutC = strdup(FileNameOut.c_str());
        FileCreator FiCr(wxT("File Creator IOC"),FileNameInC,FileNameOutC,TypeFile);
        FiCr.ShowModal();
        errors=FiCr.GetErrorFileCreation();
    }

    if(!errors){
    m_activeProject->AddFile( F_outFormatWx, wxT("src/") );//virtualFolder
    m_projectsTree->AddFile( F_outFormatWx, wxT("src/"), m_activeProject );
    }
}




void ProjectManager::AddFiles( Project *project )
{
    if( project == NULL )
    {
        if( m_activeProject != NULL )
        {
            project = m_activeProject;
        }
        else
        {
            return;
        }
    }

    wxFileDialog filedlg( NULL, wxT( "Add files" ), wxEmptyString, wxEmptyString,
                          wxFileSelectorDefaultWildcardStr, wxFD_FILE_MUST_EXIST | wxFD_OPEN | wxFD_MULTIPLE );

    int result = filedlg.ShowModal();
    if( result != wxID_OK )
    {
        return;
    }

    wxArrayString arrayPaths;
    filedlg.GetPaths( arrayPaths );
    const wxString &projectDirectory = project->GetDirectory();

    wxString virtualFolder =
        wxGetTextFromUser( wxT( "Add files to virtual folder:" ), wxT( "Add files" ), _T( "src/" ), NULL );
    if( virtualFolder.IsEmpty() )
    {
        return;
    }

    //FIXED: virtual folder path, this is necessary
    wxStringTokenizer st( virtualFolder, _T( "/\\" ), wxTOKEN_STRTOK );
    virtualFolder = wxEmptyString;
    while( st.HasMoreTokens() )
    {
        virtualFolder += st.GetNextToken() + _T( "/" );
    }

    for( size_t i=0; i<arrayPaths.Count(); ++i )
    {
        wxString filePath = arrayPaths[i];
        bool flagAddFiles = true;
        if( wxFileExists( filePath ) )
        {
            const wxString &dir = filePath.BeforeLast( _T( '/' ) ) + _T( "/" );
            if( dir != projectDirectory )
            {
                result = wxMessageBox( wxT( "The file: " ) + filePath +
                                       wxT( " is not in project directory, you want copy this file to project directory?" ),
                                       wxT( "Add files" ), wxYES | wxCANCEL | wxNO | wxICON_QUESTION );

                switch( result )
                {
                case wxYES:
                    filePath = projectDirectory + wxFileNameFromPath( filePath );
                    if( wxFileExists( filePath ) )
                    {
                        result = wxMessageBox( wxT( "The file: " ) + filePath +
                                               wxT( " already exists, you want replace this?" ),
                                               wxT( "Add files" ), wxYES | wxNO | wxCANCEL | wxICON_QUESTION );

                        if( result != wxYES )
                        {
                            flagAddFiles = false;
                        }
                    }

                    wxCopyFile( filePath, filePath );
                    break;

                case wxNO:
                    //TODO: show warning, the project maybe will have compiling problems
                    break;

                default:
                    flagAddFiles = false;
                }
            }

            if( flagAddFiles )
            {
                project->AddFile( filePath, virtualFolder );
                m_projectsTree->AddFile( filePath, virtualFolder, project );
            }
        }
    }
}

void ProjectManager::SaveProject( Project *project )
{
    if( project != NULL )
    {
        project->Save();
    }
}

void ProjectManager::SaveActiveProject()
{
    if( m_activeProject != NULL )
    {
        m_activeProject->Save();
    }
}

void ProjectManager::SaveAllProjects()
{
    std::map<wxString,Project>::iterator it;
    for( it = m_mapProjects.begin(); it != m_mapProjects.end(); ++it )
    {
        if( it->second.IsModified() )
        {
            it->second.Save();
        }
    }
}


bool ProjectManager::CloseActiveProject()
{
    return CloseProject( m_activeProject );
}

bool ProjectManager::CloseAllProjects()
{
    std::map<wxString,Project>::iterator it;
    for( it = m_mapProjects.begin(); it != m_mapProjects.end(); )
    {
        if( !CloseProject( &it->second ) )// only remove the project from the ProjectsTree
        {
            return false;
        }

        it = m_mapProjects.begin();
    }
    m_mapProjects.clear();
    m_activeProject = NULL;

    return true;
}

// only remove the project from the ProjectsTree and save the same
bool ProjectManager::CloseProject( Project *project )
{
    if( project != NULL )
    {
        if( project->IsModified() )
        {
            int result = wxMessageBox( wxT( "The project: " ) + project->GetFilename() +
                                       wxT( " has been modified, you want save this?" ),
                                       wxT( "Close project" ), wxYES | wxNO | wxCANCEL | wxICON_QUESTION );

            if( wxCANCEL == result )
            {
                return false;
            }

            if( wxYES == result )
            {
                project->Save();
            }
        }

        m_projectsTree->RemoveProject( project );
    }

    if( project->GetFilename() == m_activeProject->GetFilename() )
    {
        m_mapProjects.erase( project->GetFilename() );
        m_activeProject = NULL;
        const std::map<wxString,Project>::iterator &it = m_mapProjects.begin();
        if( it != m_mapProjects.end() )
        {
            m_activeProject = &it->second;
            m_projectsTree->ActivateProject( m_activeProject );
        }
    }
    else
    {
        m_mapProjects.erase( project->GetFilename() );
    }


    return true;
}

bool ProjectManager::OpenProject()
{
    wxFileDialog filedlg( NULL, wxT( "Open project" ), wxEmptyString, wxEmptyString,
                          _T( "Alliance files (*.") + s_projectExtension + _T( ")|*." ) + s_projectExtension, wxFD_FILE_MUST_EXIST | wxFD_OPEN );

    int result = filedlg.ShowModal();
    if( result != wxID_OK )
    {
        return false;
    }

    if( wxFileExists( filedlg.GetPath() ) )
    {
        return OpenProject( filedlg.GetPath() );
    }

    //TODO: Show message, the file not exist

    return false;
}

bool ProjectManager::OpenProject( const wxString &filename )
{
    //not open 2 times the same project
    const std::map<wxString,Project>::iterator &it = m_mapProjects.find( filename );

    if( it != m_mapProjects.end() )
    {
        m_activeProject = &(it->second);
    }
    else
    {
        m_activeProject = &( m_mapProjects[filename] = Project() );
        m_activeProject->LoadFile( filename );
        m_projectsTree->AddProject( m_activeProject );
    }

    m_projectsTree->ActivateProject( m_activeProject );

    return true;
}

bool ProjectManager::IsOpenProject( const wxString &filename ) const
{
    const std::map<wxString,Project>::const_iterator &it = m_mapProjects.find( filename );
    if( it != m_mapProjects.end() )
    {
        return true;
    }

    return false;
}

void ProjectManager::Build()
{
    if( m_activeProject != NULL )
    {
        sdk::EditorManager::Get()->SaveAllEditors();
        m_activeProject->Compile();
    }
}

void ProjectManager::BuildOptions( Project *project )
{
    if( project == NULL )
    {
        if( m_activeProject != NULL )
        {
            project = m_activeProject;
        }
        else
        {
            return;
        }
    }

    BuildOptionsDialog dlg( NULL, project );
    dlg.ShowModal();
}

Project *ProjectManager::GetProject( const wxString &filename )
{
    const std::map<wxString,Project>::iterator &it = m_mapProjects.find( filename );

    if( it != m_mapProjects.end() )
    {
        return &(it->second);
    }

    return NULL;
}

Project *ProjectManager::GetActiveProject()
{
    return m_activeProject;
}

ProjectsTree *ProjectManager::GetProjectsTree() const
{
    return m_projectsTree;
}

bool ProjectManager::OnClose()
{
    return CloseAllProjects();
}
}
